
#include "stdio.h"
#include <stdlib.h>
#include "string.h"

#include "iostream"
#include <vector>

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "calc_conv_1d.hpp"

#if defined(WIN32) || defined(WIN64) 
#include "windows.h"
#else
#include <sys/time.h>
#endif

using namespace std;

#define CONV_TEST_LENGTH 1024

void dumpArrayDataToFile(int * dataIn, int num, const char * strFileName)
{
	FILE * fp = fopen(strFileName, "w");

	char strToWrite[1024];
	strToWrite[0] = '\0';
	unsigned int sum = 0;
	for (int i = 0; i < num; i++)
	{

		sprintf(strToWrite, "%s %d", strToWrite, dataIn[i]);
		sum = sum + dataIn[i];
		if (i % 20 == 19)
		{
			sprintf(strToWrite, "%s\n", strToWrite);
		}
		if (strlen(strToWrite) >= 900)
		{
			fwrite(strToWrite, strlen(strToWrite), 1, fp);
			memset(strToWrite, 0, 1024);
		}
	}
	if (strlen(strToWrite))
	{
		fwrite(strToWrite, strlen(strToWrite), 1, fp);
	}
	printf("sum is %d\n", sum);
	fclose(fp);
}

void dumpArrayDataToFile(float * dataIn, int num, const char * strFileName)
{
	FILE * fp = fopen(strFileName, "w");

	char strToWrite[1024];
	strToWrite[0] = '\0';
	for (int i = 0; i < num; i++)
	{

		sprintf(strToWrite, "%s %d", strToWrite, (int)dataIn[i]);
		if (i % 20 == 19)
		{
			sprintf(strToWrite, "%s\n", strToWrite);
		}
		if (strlen(strToWrite) >= 900)
		{
			fwrite(strToWrite, strlen(strToWrite), 1, fp);
			memset(strToWrite, 0, 1024);
		}
	}
	if (strlen(strToWrite))
	{
		fwrite(strToWrite, strlen(strToWrite), 1, fp);
	}
	fclose(fp);
}


void setTestDataForConv(int * a, int * b, int * conbined, unsigned int * countA, unsigned int *countB, unsigned int* pitchA)
{
	for (int i = 0; i < 1024; i++)
	{
		a[i] = i + 1;
		b[i] = i + 1025;
	}

	*countA = 1024;
	*countB = 1024;

	*pitchA = (*countA + 7) / 8 * 8;

	memcpy(conbined, a, (*countA)*sizeof(int));

	memcpy(conbined + (*pitchA), b, (*countB)*sizeof(int));
}

void conv(vector<int> &uIn, vector<int>&vIn, vector<int>&convOut)
{
	vector<int>::iterator itU = uIn.begin();
	vector<int>::iterator itV = vIn.begin();

	convOut.clear();


	int uSize = uIn.size();
	int vSize = vIn.size();
	int numResult = uSize + vSize - 1;
	convOut.resize(numResult);

	for (int i = 0; i<numResult; i++)
	{
		convOut[i] = 0;
		for (int j = 0; j <= i; j++)
		{
			int k = i - j;
			int tempU = j<uSize ? uIn[j] : 0;
			int tempV = k<vSize ? vIn[k] : 0;
			convOut[i] += tempU*tempV;
		}
	}
}


void setTestDataForConv(vector<int>& u, vector<int>& v)
{
	const int lengthToCalc = CONV_TEST_LENGTH;
	u.clear();
	v.clear();
	u.resize(lengthToCalc);
	v.resize(lengthToCalc);
	for (int i = 0; i < lengthToCalc; i++)
	{
		u[i] = i + 1;
		v[i] = i + 1025;
	}
}

void dumpVectorDataToFile(vector<int>&dataIn, const char * strFileName)
{
	FILE * fp = fopen(strFileName, "w");

	char strToWrite[1024];
	strToWrite[0] = '\0';
	unsigned int sum = 0;
	int num = dataIn.size();
	for (int i = 0; i < num; i++)
	{

		sprintf(strToWrite, "%s %d", strToWrite, dataIn[i]);
		sum = sum + dataIn[i];
		if (i % 20 == 19)
		{
			sprintf(strToWrite, "%s\n", strToWrite);
		}
		if (strlen(strToWrite) >= 900)
		{
			fwrite(strToWrite, strlen(strToWrite), 1, fp);
			memset(strToWrite, 0, 1024);
		}
	}
	if (strlen(strToWrite))
	{
		fwrite(strToWrite, strlen(strToWrite), 1, fp);
	}
	printf("sum is %d\n", sum);
	fclose(fp);
}

double getCpuClock(void)
{
#if defined(WIN32) || defined(WIN64)
	LARGE_INTEGER freq;
	LARGE_INTEGER count;
	QueryPerformanceFrequency(&freq);

	QueryPerformanceCounter(&count);

	//cout << "freq:" << freq.QuadPart << ",count:" << count.QuadPart << endl;

	return ((double)count.QuadPart) / ((double)freq.QuadPart);
#else
	struct timeval tvTemp;
	gettimeofday(&tvTemp, NULL);
	return (double)(tvTemp.tv_sec) + double(tvTemp.tv_usec) / 1000000;
#endif
}

#define BLOCK_NUM_TO_ALLOC 20

int main(int argc, char *argv[])
{
	cudaSetDevice(0);
	uchar * g_dev_buff = NULL;
	cudaError_t retError = cudaMalloc(&g_dev_buff, 1024 * 1024 * BLOCK_NUM_TO_ALLOC);
	if (retError != 0)
	{
		return retError;
	}

	double dStart = 0;
	double dEnd = 0;
	double sumTime = 0;
	double avgTime = 0;

	int * testConvA = (int *)malloc(1024 * sizeof(int));
	int * testConvB = (int *)malloc(1024 * sizeof(int));
	int * testConvConbined = (int *)malloc(4096 * sizeof(int));
	int * testConvOut = (int *)malloc(4096 * sizeof(int));

	memset(testConvOut, 0, 4096 * sizeof(int));
	unsigned int countA, countB, pitchA;
	setTestDataForConv(testConvA, testConvB, testConvConbined, &countA, &countB, &pitchA);
	uchar * devBuffConvStart = g_dev_buff;
	cudaMemcpy(devBuffConvStart, testConvConbined, (pitchA + countB)*sizeof(int), cudaMemcpyHostToDevice);
	int pitchB = ((countB + 7) >> 3) << 3;
	sumTime = 0;
	for (int i = 0; i < 20; i++)
	{
		dStart = getCpuClock();
		calc_conv_cuda((int*)devBuffConvStart, CONV_TEST_LENGTH, (int*)(devBuffConvStart + pitchA*sizeof(int)), CONV_TEST_LENGTH, devBuffConvStart + pitchA*sizeof(int)+pitchB*sizeof(int), testConvOut);
		dEnd = getCpuClock();
		sumTime += dEnd - dStart;
	}

	printf("cuda conv cost %fs\n", sumTime / 20);

	dumpArrayDataToFile((int *)testConvOut, (CONV_TEST_LENGTH<<1) - 1, "cudaConv.txt");

	free(testConvA);
	free(testConvB);
	free(testConvConbined);
	free(testConvOut);

	vector<int> uIn, vIn, convOut;

	setTestDataForConv(uIn, vIn);
	sumTime = 0;
	for (int i = 0; i < 20; i++)
	{
		dStart = getCpuClock();
		conv(uIn, vIn, convOut);
		dEnd = getCpuClock();
		sumTime += dEnd - dStart;
	}

	printf("cpu conv cost %fs\n", sumTime / 20);
	dumpVectorDataToFile(convOut, "cpu_conv.txt");
	
	cudaFree(g_dev_buff);
}